import { HttpEventType, HttpResponse } from '@angular/common/http';
import { Component, EventEmitter, OnDestroy, Output } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';

import { ConfirmModalService, FileService } from '@core/base';
import { Metadata, LocalService } from '@core/oss/local';
import { SingleInputModalService } from '@share/base/component';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzUploadFile } from 'ng-zorro-antd/upload';

interface FileUploadInfo {
  id: number;
  isUpload: boolean;
  file: any;
  metadata: Metadata;
  state: string;
  percent: number;
  onCancelRequest: Subject<void>;
}

@Component({
  selector: 'app-local-upload',
  templateUrl: './local-upload.component.html',
  styleUrls: ['./local-upload.component.less'],
  providers: [SingleInputModalService]
})
export class LocalUploadComponent implements OnDestroy {
  @Output() readonly uploadEvent: EventEmitter<Metadata> = new EventEmitter<Metadata>();
  @Output() readonly removeEvent: EventEmitter<Metadata> = new EventEmitter<Metadata>();
  @Output() readonly renameEvent: EventEmitter<Metadata> = new EventEmitter<Metadata>();
  fileUploadList: FileUploadInfo[] = [];
  index = 0;

  constructor(
    private confirmModalSrc: ConfirmModalService,
    private filesSrc: FileService,
    private localSrc: LocalService,
    private singleInputModalSrc: SingleInputModalService,
    private messageSrc: NzMessageService
  ) {}

  beforeUpload = (file: NzUploadFile): boolean => {
    const fileUploadInfo: FileUploadInfo = {
      id: this.index++,
      isUpload: false,
      file: file,
      state: '',
      metadata: new Metadata(),
      percent: 0,
      onCancelRequest: new Subject<void>()
    };

    this.fileUploadList.push(fileUploadInfo);
    this.upload(fileUploadInfo);
    return false;
  };

  view(info: FileUploadInfo): void {
    if (info.metadata.id) {
      this.localSrc.download(info.metadata.id).subscribe(data => this.filesSrc.openInTab(data));
    }
  }

  rename(info: FileUploadInfo): void {
    if (!info.metadata.id) {
      return;
    }

    const modal = this.singleInputModalSrc.open('修改文件名', { minlength: 3, value: info.metadata.name });
    modal.afterClose.subscribe(data => {
      if (data != undefined && info.metadata.id) {
        this.localSrc.updateMetadataById(info.metadata.id, data).subscribe(() => {
          info.metadata.name = data;
          this.renameEvent.emit(info.metadata);
          this.messageSrc.success('修改文件名成功');
        });
      }
    });
  }

  cancel(info: FileUploadInfo): void {
    info.onCancelRequest.next();
    info.isUpload = false;
    info.state = 'cancel';
    info.percent = 0;
  }

  continue(info: FileUploadInfo): void {
    this.upload(info);
  }

  delete(info: FileUploadInfo): void {
    if (info.metadata.id) {
      const metadataId = info.metadata.id;
      this.confirmModalSrc.danger('确定删除已上传的文件吗？').subscribe(ok => {
        if (ok) {
          this.localSrc.delete(metadataId).subscribe(() => {
            this.fileUploadList = this.fileUploadList.filter(e => e.id !== info.id);
            this.removeEvent.emit(info.metadata);
          });
        }
      });
    } else {
      this.fileUploadList = this.fileUploadList.filter(e => e.id !== info.id);
    }
  }

  upload(fileUploadInfo: FileUploadInfo): void {
    const formData = new FormData();
    formData.append('file', fileUploadInfo.file);

    fileUploadInfo.state = 'processing';
    fileUploadInfo.isUpload = true;

    this.localSrc
      .uploadEvent(formData)
      .pipe(takeUntil(fileUploadInfo.onCancelRequest))
      .subscribe({
        next: (e: any) => {
          if (e.type === HttpEventType.UploadProgress) {
            fileUploadInfo.percent = Math.round((100 * e.loaded) / e.total);
          } else if (e instanceof HttpResponse) {
            fileUploadInfo.state = 'success';
            fileUploadInfo.isUpload = false;
            fileUploadInfo.metadata = new Metadata(e.body);

            this.uploadEvent.emit(fileUploadInfo.metadata);
          }
        },

        error: (err: any) => {
          fileUploadInfo.state = 'error';
          fileUploadInfo.isUpload = false;
        }
      });
  }

  ngOnDestroy(): void {
    this.fileUploadList.forEach(e => {
      e.onCancelRequest.next();
      e.onCancelRequest.complete();
    });
  }
}
